//
// This file is part of cpp-lib, a library of C++ functions and classes.
// Cpp-lib is copyright (C) 2004 and onwards, The CPL Interest Group.
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this library (see the file COPYING); if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
// 02111-1307  USA
//
// $Id$
//

#include <iostream>
#include <exception>
#include <stdexcept>

#include "cpp-lib/registry.h"
#include "cpp-lib/registry-crypt.h"
#include "cpp-lib/matrix.h"
#include "cpp-lib/matrix-io.h"
#include "cpp-lib/network.h"
#include "cpp-lib/util.h"
#include "cpp-lib/blowfish.h"
#include "cpp-lib/sys/util.h"

#include "test_key.h"


std::string const conf_file        = "registry-test.conf" ;
std::string const conf_file_matlab = "registry-test.m"    ;

using namespace cpl::util          ;
using namespace cpl::util::network ;
using namespace cpl::math          ;
using namespace cpl::crypt         ;

typedef matrix< double , fixed< 3 , 3 > > mat_3_t ;


void output( registry const& reg ) {

  //
  // The get< T > template returns the value (which must be of type T)
  // associated with the key.
  //

  std::cout << "logging to "   << reg.get< std::string >( "logfile" ) << '\n' ;
  std::cout << "using engine " << reg.get< std::string >( "engine"  ) << '\n' ;

  //
  // There are various check_xyz() members which check that the value
  // associated with the key has certain properties (e.g., is a bool,
  // is a number, ...)
  //

  if( reg.is_set( "flag" ) && reg.check_bool( "flag" ) ) 
  { std::cout << "Fly the flag!!!\n" ; }

  std::string const host = reg.get< std::string >( "host_2" ) ;

  //
  // check_long() checks for an integer in a given range.
  //

  long const port = check_long( reg.check_nonneg( "port" ) , 0 , 65535 ) ;


  std::cout << "pi = " << reg.check_positive( "magic_constant" ) << '\n' ;


  //
  // as_matrix() returns a proxy usable within the ET framework (in this
  // case, it checks for a 3x3-matrix).
  //

  mat_3_t const M = as_matrix
  ( reg.check_vector_vector_double( "matrix" , 3 , 3 ) ) ;

  std::cout << "matrix =\n"            <<            M   ;
  std::cout << "inverse( matrix ) =\n" << inverse_3( M ) ;

  // Yes, you can use a reference here!

  std::vector< boost::any > const& v = reg.check_vector_any( "list" , 3 ) ;

  //
  // The convert< T > template can be used to access the value stored
  // in a boost::any.  It will throw a human-readable error message if
  // the stored object's type does not match T.
  //

  try { 
    
    std::string const& s = convert< std::string >( v[ 1 ] ) ;
    std::cout << "second element of list is: " << s << '\n' ;

  }
  catch( std::runtime_error const& e ) { 
    
    // 
    // reg.key_defined_at( key ) returns a message indicating where key 
    // was defined.
    //

    throw std::runtime_error
    ( "second element of " + reg.key_defined_at( "list" ) + ": " + e.what() ) ; 
  
  }

}


int main() {

  try {

  registry reg ;
  
  blowfish bf( key() ) ;
  read_encrypted_configuration( reg , bf , IV() , conf_file , ".crypt" ) ;
  std::cout << conf_file + ".crypt" << " or " << conf_file << ":\n" ;
  output( reg ) ;

  reg.clear() ;
  std::cout << conf_file + ".var" << ":\n" ;
  reg.read_from( conf_file + ".var" , config_style() ) ;
  output( reg ) ;

  reg.clear() ;
  std::cout << "conf_file_matlab" << ":\n" ;
  reg.read_from( conf_file_matlab , matlab_style() , false ) ;
  std::cout 
	<< "string = '" 
	<< reg.get< std::string >( "string" ) 
	<< "'\n"
	<< "list has " 
	<< reg.check_vector_double( "list" ).size() 
	<< " elements.\n"
	;

  } catch( std::runtime_error const& e )
  { die( e.what() ) ; }

}
